const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const types = require("@babel/types");
const generator = require("@babel/generator").default;

const jscode = `!function(a,b) {c = a | b;}(111,222);`; //转换为i var b=3;

const ast = parser.parse(jscode);
const visitor = {
    // CallExpression(path){
    //     let callee=path.node.callee //函数主体
    //     let argum=path.node.arguments //调用参数
    //     if (!types.isFunctionExpression(callee) || arguments.length===0){return;};
    //     let params=callee.params;
    //     path.traverse({
    //         Identifier(_path){
    //             if (!types.isBinaryExpression(_path.parent)){return;};
    //             for (let i =0;i<argum.length;i++){ //遍历实参,因为形参可能会比实参长;
    //                 let arg = params[i];
    //                 let name=arg.name;
    //                 if (name==_path.node.name){
    //                     _path.replaceWith(argum[i])
    //                 }
    //             }
    //         }
    //     })
    //     argum.length=0;
    //     params.length=0;
    //
    // }
    CallExpression(path) {
        let callee = path.get("callee");
        let arguments = path.get('arguments');
        if (!callee.isFunctionExpression() || arguments.length === 0) { //这里实参的长度判断可以写死。
            return;
        }
        //对着网站解析，获取形参
        let params = callee.get('params');
        let scope = callee.scope;
        for (let i = 0; i < arguments.length; i++) { //遍历实参，因为形参可能比实参长。
            let arg = params[i];
            let {
                name
            } = arg.node;
            const binding = scope.getBinding(name); // 在当前作用域中，找到参数的引用
            if (!binding || binding.constantViolations.length > 0) { //形参有被改变，不能被还原
                continue; // 表示在当前作用域中的引用，因为现在的作用域是整个函数，所有如果'!function(a,b) {c = a | b;a=1}(111,222);'或者'!function(a,b) {a=1;c = a | b}(111,222);'是无法修改a的值的
            }
            for (refer_path of binding.referencePaths) { //因为是字面量，所以直接替换，但是遇到Array类型的需要另外处理
                //也无非获取父节点，然后判断索引
                refer_path.replaceWith(arguments[i]);
                //上面的参数可以是path，也可以是node。但是我遇到过path报错，node可以的情况。
            }
            arg.remove();
            arguments[i].remove();
        }
    },
}

traverse(ast, visitor);
console.log(generator(ast)['code'])